//
// Created by 郝羽 on 2018/2/7.
//

#ifndef RTC_XSWITCH2_DTW_HPP
#define RTC_XSWITCH2_DTW_HPP
extern "C" {
#include <libavcodec/avcodec.h>
};

#include <iostream>
#include <cmath>
#include <cstdint>
#include <limits>
#include <climits>
#include <cfloat>


class DTW {
    int max_m;
    int max_n;
    int window;

    double** dtw;

public:
    DTW(int sample_rate, int max_frame_msec, int window_msec) {
        max_m = max_frame_msec * sample_rate / 1000;
        max_n = max_frame_msec * sample_rate / 1000;
        window = window_msec * sample_rate / 1000;

        dtw = new double*[max_m+1];
        for (int i = 0; i < max_m+1; i++) {
            dtw[i] = new double[max_n+1];
        }
    }
    // f1 contains m samples
    // f2 contains n samples
    int Distance(double* distance, AVFrame* f1, AVFrame* f2) {
        int m = f1->nb_samples;
        int n = f2->nb_samples;
        int w = std::max(window, std::abs(m - n));
        if (m > max_m || n > max_n) {
            std::clog << "frame contains too many samples" << std::endl;
            return -1;
        }

        for (int i = 0; i < m+1; i++) {
            dtw[i][0] = DBL_MAX;
        }
        for (int j = 0; j < n+1; j++) {
            dtw[0][j] = DBL_MAX;
        }
        dtw[0][0] = 0.0;

        int16_t* sp1 = (int16_t*)f1->data[0];
        int16_t* sp2 = (int16_t*)f2->data[0];

        for (int i = 1; i <= m; i++) {
            int16_t s1 = sp1[i-1];
            for (int j = std::max(1, i - w); j <= std::min(n, i + w); j++) {
                int16_t s2 = sp2[j-1];
                double nd = (double)(s1 - s2);
                nd /= 32767;
                dtw[i][j] = std::pow((double)(s1 - s2) / 32767,2) + std::min(std::min(dtw[i-1][j], dtw[i][j-1]), dtw[i-1][j-1]);
            }
        }
        *distance = dtw[m][n] / std::max(m, n);
        return 0;
    }
};
#endif //RTC_XSWITCH2_DTW_HPP
